package com.dxhackday.ppicker.model;

import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.FilteredImageSource;
import java.awt.image.ImageFilter;
import java.awt.image.ImageProducer;
import java.util.HashMap;

/**
 * @author Sergey Lyadzhin
 * @since 29.11.12 23:58
 */
public class ImageHashCalculator {
    public static final int SIMILARITY_THRESHOLD = 15;

    public static boolean isSimilar(BufferedImage img1, BufferedImage img2) {
        long hash1 = new ImageHashCalculator(img1).calculate();
        long hash2 = new ImageHashCalculator(img2).calculate();
        return getBitDistance(hash1, hash2) < SIMILARITY_THRESHOLD;
    }

    private static int getBitDistance(long a, long b) {
        int result = 0;
        for (int i = 0; i < Long.SIZE; i++) {
            long bitA = a & (1L << i);
            long bitB = b & (1L << i);
            result += (bitA == bitB) ? 0 : 1;
        }
        return result;
    }

    private final BufferedImage img;

    public ImageHashCalculator(BufferedImage img) {
        this.img = img;
    }

    public long calculate() {
        int size = 8;
        java.awt.Image scaledImage = getScaledImage(img, size);
        BufferedImage grayImage = getGrayedImage(size, scaledImage);

        double total = 0;
        for (int x = 0; x < size; x++) {
            for (int y = 0; y < size; y++) {
                int i = grayImage.getRGB(x, y);
                total += i;
            }
        }
        int avg = (int)(total / 64);

        long hash = 0;
        int index = 0;
        for (int x = 0; x < size; x++) {
            for (int y = 0; y < size; y++) {
                int i = grayImage.getRGB(x, y);
                if (i > avg) {
                    hash |= (1L << index);
                }
                index++;

            }
        }
        return hash;
    }

    private static BufferedImage getGrayedImage(int size, java.awt.Image scaledImage) {
        BufferedImage grayImage = new BufferedImage(size, size, BufferedImage.TYPE_BYTE_GRAY);
        Graphics2D g1 = grayImage.createGraphics();
        g1.drawImage(scaledImage, 0, 0, size, size, null);
        g1.dispose();
        return grayImage;
    }

    private static BufferedImage getGrayedImage2(int size, java.awt.Image scaledImage) {
        ImageFilter filter = new GrayFilter(true, 50);
        ImageProducer producer = new FilteredImageSource(scaledImage.getSource(), filter);
        java.awt.Image image = Toolkit.getDefaultToolkit().createImage(producer);
        BufferedImage grayImage = new BufferedImage(size, size, BufferedImage.TYPE_BYTE_GRAY);
        Graphics2D g1 = grayImage.createGraphics();
        g1.drawImage(image, 0, 0, size, size, null);
        g1.dispose();
        return grayImage;
    }

    private static java.awt.Image getScaledImage(BufferedImage image, int size) {
        BufferedImage scaledImage = new BufferedImage(size, size, image.getType());
        Graphics2D g2 = scaledImage.createGraphics();
        HashMap<Object, Object> hints = new HashMap<>();
        hints.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
        hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        hints.put(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
        hints.put(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
        hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        hints.put(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
        g2.setRenderingHints(hints);
        g2.drawImage(image, 0, 0, size, size, null);
        g2.dispose();
        return scaledImage;
    }

    // too long
    private static java.awt.Image getScaledImage2(BufferedImage image, int size) {
        return image.getScaledInstance(size, size, java.awt.Image.SCALE_SMOOTH);
    }
}
